package com.github.io.listener;

import com.github.io.annotation.Api;
import com.github.io.annotation.ApiOperateNote;
import com.github.io.utils.Constant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.core.MethodParameter;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.*;

/**
 * @author liry
 * @version 1.0
 * @date Created on 2022/8/26 16:27
 * Description:  自定义事件监听器实现获取控制器下所有接口信息功能;
 * 思想: 在系统加载完成时，通过应用上下文信息 ApplicationContext， 获取 实现RequestMappingHandlerMapping类 所有控制类信息
 */
@Component
public class RequestMappingScanListener implements ApplicationListener<ApplicationContextEvent> {
    
    private RedisTemplate<String, Object> redisTemplate;
    public RequestMappingScanListener(RedisTemplate redisTemplate){
        this.redisTemplate = redisTemplate;
    }


    @Override
    public void onApplicationEvent(ApplicationContextEvent applicationContextEvent) {
        // 获取应用上下文信息
        ApplicationContext applicationContext = applicationContextEvent.getApplicationContext();

        // 获取　RequestMappingHandlerMapping 实例对象
        RequestMappingHandlerMapping requestMappingHandlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class);

        
        // 请求映射器执行映射器获取 执行器方法集合
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();

        // 获取执行器方法所有key
        Set<RequestMappingInfo> requestMappingInfos = handlerMethods.keySet();

        List<Map<String, String>> apis = new ArrayList<>();
        requestMappingInfos.forEach(requestMappingInfo -> {
            HandlerMethod handlerMethod = handlerMethods.get(requestMappingInfo);

            // 获取方法请求url
            PatternsRequestCondition patternsCondition = requestMappingInfo.getPatternsCondition();
            String methodUrls = getMethodUrls(patternsCondition);
            
            // 获取方法请求方式
            RequestMethodsRequestCondition methodsCondition = requestMappingInfo.getMethodsCondition();
            String methods = getMethods(methodsCondition);


            // 获取Request类型: application/json、application/xml
            ProducesRequestCondition producesCondition = requestMappingInfo.getProducesCondition();
            String mediaTypes = getMediaTypes(producesCondition, handlerMethod);


            // 获取方法参数信息
//            MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
//            if (methodParameters != null) {
//                for (MethodParameter methodParameter : methodParameters) {
//                    Parameter parameter = methodParameter.getParameter();
//                    Class<?> type = parameter.getType();
//                    String name1 = parameter.getName();
//                    System.out.println("type: " + type + ", name1: " + name1);
//                }
//            }

            // 获取接口控制类名
            Class<?> beanType = handlerMethod.getBeanType();
            String beanName = beanType.getName();
            // 获取接口方法名
            String methodName = handlerMethod.getMethod().getName();

            // 获取接口返回值类型
            MethodParameter returnType = handlerMethod.getReturnType();
            Class<?> returnParameterType = returnType.getParameterType();
            String returnParameterTypeName = returnParameterType.getName();


            // 获取控制器上注解信息
            Api apiAnnotation = beanType.getAnnotation(Api.class);
            String handlerNote = "";
            if (apiAnnotation != null) {
                // 获取控制器说明
                handlerNote = apiAnnotation.value();
            }

            // 获取接口方法上注解信息
            ApiOperateNote apiOperateNoteAnnotation = handlerMethod.getMethodAnnotation(ApiOperateNote.class);
            String operateNotes = "", operateValue = "";
            if (apiOperateNoteAnnotation != null) {
                operateNotes = apiOperateNoteAnnotation.note();
                operateValue = apiOperateNoteAnnotation.value();
            }
            

            Map<String, String> api = new HashMap<>();
            api.put("methodUrls", methodUrls);
            api.put("methods", methods);
            api.put("mediaTypes", mediaTypes);
            api.put("beanName", beanName);
            api.put("methodName", methodName);
            api.put("returnParameterTypeName", returnParameterTypeName);
            api.put("handlerNote", handlerNote);
            api.put("operateNote", operateNotes);
            api.put("operateValue",operateValue);
            
            apis.add(api);
        });

        redisTemplate.opsForValue().set(Constant.API_SOURCE_KEY, apis);
    }


    // 获取请求参数方式
    private String getMediaTypes(ProducesRequestCondition producesCondition, HandlerMethod handlerMethod) {
        Set<MediaType> producibleMediaTypes = producesCondition.getProducibleMediaTypes();
        producibleMediaTypes.forEach(v -> {
            MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
            for (MethodParameter methodParameter : methodParameters) {
                if (methodParameter.hasMethodAnnotation(RequestBody.class)) {
                    producibleMediaTypes.add(MediaType.APPLICATION_JSON);
                }
            }
        });

        StringBuilder stringBuilder = new StringBuilder();
        producibleMediaTypes.forEach(v -> {
            stringBuilder.append(v.getType()).append(",");
        });

        if (producibleMediaTypes.size() > 0) {
            stringBuilder.deleteCharAt(stringBuilder.length() - 1);
        }
        return stringBuilder.toString();
    }

    // 获取接口上url
    private String getMethodUrls(PatternsRequestCondition patternsCondition) {
        // 将 Set集合数据转成 String 
        StringBuilder stringBuilder = new StringBuilder();
        Set<String> patterns = patternsCondition.getPatterns();
        if (patterns != null) {
            patterns.forEach(v -> {
                stringBuilder.append(v).append(",");
            });
        }

        if (patterns.size() > 0) {
            stringBuilder.deleteCharAt(stringBuilder.length() - 1);
        }
        return stringBuilder.toString();
    }


    // 获取接口上请求方式
    private String getMethods(RequestMethodsRequestCondition methodsCondition) {
        StringBuilder stringBuilder = new StringBuilder();
        Set<RequestMethod> methods = methodsCondition.getMethods();

        methods.forEach(v -> {
            stringBuilder.append(v.name()).append(",");
        });

        if (methods.size() > 0) {
            stringBuilder.deleteCharAt(stringBuilder.length() - 1);
        }

        return stringBuilder.toString();
    }
}
